package driver

import (
	"errors"
	"fmt"
	"log"
	"os"
	"strings"

	"cloudea.com/k8s-csi-fs/driver/proto"
	"cloudea.com/k8s-csi-fs/driver/utils"
)

type REPO_TPE = string

const (
	REPO_TYPE_NFS REPO_TPE = "NFS"
	REPO_TYPE_SSH REPO_TPE = "SSH"
	REPO_TYPE_S3  REPO_TPE = "S3"
)

/** 在仓库中创建一个卷 */
func CreateVolume(name string, capacity int) error {
	volumePath := getVolumePath(name)
	//blockCount := capacity //这样可以过所有用例，但是太慢了，不采用
	blockCount := capacity / M
	if capacity%M != 0 {
		blockCount += 1
	}
	// 如果卷已经存在，则直接返回
	if utils.FileExists(volumePath) {
		return nil
	}
	// 使用dd命令创建空白文件
	//return utils.Shell(fmt.Sprintf("dd if=/dev/zero of=%s bs=1c count=%d", volumePath, blockCount))
	return utils.Shell(fmt.Sprintf("dd if=/dev/zero of=%s bs=M count=%d", volumePath, blockCount))
}

/** 删除卷文件 */
func DeleteVolume(name string) error {
	volumePath := getVolumePath(name)
	// 如果卷不存在，则直接返回
	if name == "" || !utils.FileExists(volumePath) {
		return nil
	}
	return os.Remove(volumePath)
}

/** 获取卷信息 */
func GetVolume(name string) (*proto.Volume, error) {
	volumePath := getVolumePath(name)
	if !utils.FileExists(volumePath) {
		return nil, errors.New("卷不存在")
	}
	info := &proto.Volume{
		CapacityBytes: utils.FileSize(volumePath),
		VolumeId:      name,
	}
	return info, nil
}

/** 列出所有卷 */
func ListVolumes() ([]proto.Volume, error) {
	entries, err := os.ReadDir(repo_dir)
	if err != nil {
		return nil, errors.New("读取仓库目录出错")
	}
	var volumes []proto.Volume
	for _, entry := range entries {
		if !entry.IsDir() && strings.HasSuffix(entry.Name(), repo_volume_suffix) {
			before, _ := strings.CutSuffix(entry.Name(), repo_volume_suffix)
			info, err := entry.Info()
			if err != nil {
				return nil, errors.New("读取仓库卷文件信息出错")
			}
			volumes = append(volumes, proto.Volume{
				CapacityBytes: info.Size(),
				VolumeId:      before,
			})
		}
	}

	return volumes, nil
}

/** 获取仓库空闲大小 */
func GetAvailableCapacity() int64 {
	return int64(utils.DirFreeSize(repo_dir))
}

/** 格式化卷 */
func FormatVolume(name string) error {
	volumePath := getVolumePath(name)
	return utils.Shell("mkfs.ext4 " + volumePath)
}

/** 挂载卷 */
func MountVolume(name string, target string) error {
	err := os.MkdirAll(target, os.ModePerm)
	if err != nil {
		log.Println("创建目录失败:", target)
		return err
	}
	volumePath := getVolumePath(name)
	if utils.IsMounted(target) {
		log.Println("已挂载，无需重复挂载")
		return nil
	}
	cmd := fmt.Sprintf("mount -o loop %s %s", volumePath, target)
	log.Println(cmd)
	return utils.Shell(cmd)
}

/** 取消挂载卷 */
func UnMountVolume(name string, target string) error {
	volumePath := getVolumePath(name)
	var err error = nil
	if utils.IsMounted(target) {
		err = utils.Shell("umount -f " + volumePath) // 特地缷载源目录
	} else {
		log.Println("已取消挂载，无需重复取消挂载")
	}
	os.Remove(target)
	return err
}

/** 获取卷的文件位置 */
func getVolumePath(name string) string {
	return repo_dir + "/" + name + repo_volume_suffix
}

var repo_type = REPO_TYPE_S3
var repo_dir = "/root/.k8s-csi-fs"
var repo_nfs_uri = "127.0.0.1:/nfsroot"
var repo_ssh_uri = "cloudea@192.168.39.1:/home/cloudea/test/volume"
var repo_ssh_pass = "15115103140"
var repo_volume_suffix = ".volume"
var CSIEndPoint = "/tmp/csi.sock"
var repo_s3_uri = "http://192.168.39.1:9000/"
var repo_s3_bucket = "test"
var repo_s3_access_key = "minioadmin"
var repo_s3_secret_key = "minioadmin"

/** 卷仓库初始化 */
func init() {
	// 保证环境变量存在
	if os.Getenv("REPO_TYPE") != "" {
		repo_type = os.Getenv("REPO_TYPE")
	}
	if os.Getenv("REPO_DIR") != "" {
		repo_dir = os.Getenv("REPO_DIR")
	}
	if os.Getenv("REPO_NFS_URI") != "" {
		repo_nfs_uri = os.Getenv("REPO_NFS_URI")
	}
	if os.Getenv("REPO_SSH_URI") != "" {
		repo_ssh_uri = os.Getenv("REPO_SSH_URI")
	}
	if os.Getenv("REPO_SSH_PASS") != "" {
		repo_ssh_pass = os.Getenv("REPO_SSH_PASS")
	}
	if os.Getenv("REPO_VOLUME_SUFFIX") != "" {
		repo_volume_suffix = os.Getenv("REPO_VOLUME_SUFFIX")
	}
	if os.Getenv("CSI_ENDPOINT") != "" {
		CSIEndPoint = os.Getenv("CSI_ENDPOINT")
	}
	if os.Getenv("REPO_S3_URI") != "" {
		repo_s3_uri = os.Getenv("REPO_S3_URI")
	}
	if os.Getenv("REPO_S3_BUCKET") != "" {
		repo_s3_bucket = os.Getenv("REPO_S3_BUCKET")
	}
	if os.Getenv("REPO_S3_ACCESS_KEY") != "" {
		repo_s3_access_key = os.Getenv("REPO_S3_ACCESS_KEY")
	}
	if os.Getenv("REPO_S3_SECRET_KEY") != "" {
		repo_s3_secret_key = os.Getenv("REPO_S3_SECRET_KEY")
	}

	// 保证库目录存在
	//utils.AssertMsg(os.MkdirAll(repo_dir, os.ModePerm), "无法创建挂载目录："+repo_dir)
	// 上面命令在容器中无效，可能是go1.20的一个bug
	if !utils.FileExists(repo_dir) {
		utils.AssertMsg(utils.Shell("mkdir "+repo_dir), "无法创建挂载目录："+repo_dir)
	}
	if !utils.FileExists(repo_dir) {
		log.Fatalln(repo_dir + "无法被创建")
	} else {
		log.Println(repo_dir + "已存在")
	}
	// 保证挂载目录干净
	tryCount := 0
	for utils.IsMounted(repo_dir) {
		utils.AssertMsg(utils.Shell("umount -l "+repo_dir), "umount 错误")
		tryCount++
		if tryCount > 10 {
			panic("不能重复挂载")
		}
	}
	// 将远程目录挂载到库目录
	if repo_type == REPO_TYPE_NFS {
		// utils.AssertMsg(unix.Mount(repo_ip+":/nfsroot", repo_dir, "nfs", 0, "nolock"), "挂载失败")
		mountStatement := fmt.Sprintf("mount -t nfs -o nolock %s %s", repo_nfs_uri, repo_dir)
		utils.AssertMsg(utils.Shell(mountStatement), "挂载失败")
		log.Println("成功挂载NFS磁盘")
	} else if repo_type == REPO_TYPE_SSH {
		// -o ssh_command='sshpass -p 123456 ssh'
		mountStatement := fmt.Sprintf("sshfs -o ssh_command='sshpass -p %s ssh -o StrictHostKeyChecking=no',allow_other,default_permissions %s %s", repo_ssh_pass, repo_ssh_uri, repo_dir)
		utils.AssertMsg(utils.Shell(mountStatement), "挂载失败")
		log.Println("成功挂载SSHFS磁盘")
	} else if repo_type == REPO_TYPE_S3 {
		// 使用s3fs有bug,协议不对会导致终端卡死
		utils.AssertMsg(utils.Shell(fmt.Sprintf("echo %s:%s > /etc/passwd-s3fs", repo_s3_access_key, repo_s3_secret_key)), "创建密码文件失败")
		utils.AssertMsg(utils.Shell("chmod 600 /etc/passwd-s3fs"), "更新权限失败")
		mountStatement := fmt.Sprintf("s3fs %s %s -o passwd_file=/etc/passwd-s3fs -o url=%s -o use_path_request_style", repo_s3_bucket, repo_dir, repo_s3_uri)
		utils.AssertMsg(utils.Shell(mountStatement), "挂载失败")
		log.Println("成功挂载S3桶作为磁盘")
		// 换用goofys， 但goofys无论如何不能连
		// utils.AssertMsg(utils.Shell("mkdir -p $HOME/.aws"), "创建配置文件目录失败")
		// utils.AssertMsg(utils.Shell(fmt.Sprintf("echo \"[default]\\naws_access_key_id = %s\\naws_secret_access_key = %s\" > $HOME/.aws/credentials", repo_s3_access_key, repo_s3_secret_key)), "创建密码文件失败")
		// mountStatement := fmt.Sprintf("./goofys --dir-mode 777 --file-mode 777 --endpoint %s %s %s", repo_s3_uri, repo_s3_bucket, repo_dir)
		// utils.AssertMsg(utils.Shell(mountStatement), "挂载失败")
		// log.Println("成功挂载S3桶作为磁盘")

	}
}
